package ch.fusun.baron.serverapp.view;

import java.util.ArrayList;
import java.util.List;

import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.Platform;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.part.ViewPart;

import ch.fusun.baron.core.injection.Inject;
import ch.fusun.baron.core.injection.ReInjector;
import ch.fusun.baron.core.rmi.User;
import ch.fusun.baron.core.service.UserListener;
import ch.fusun.baron.core.service.UserService;
import ch.fusun.baron.data.DataUpdate;
import ch.fusun.baron.serverapp.Activator;
import ch.fusun.baron.turn.TurnListener;
import ch.fusun.baron.turn.TurnService;

/**
 * A view to show all the logged in users
 */
public class UsersView extends ViewPart implements TurnListener, UserListener {
	/**
	 * The View ID
	 */
	public static final String ID = "ch.fusun.baron.serverapp.usersview"; //$NON-NLS-1$

	private TreeViewer viewer;

	private ViewContentProvider contentProvider;

	private UserService userService;
	private TurnService turnService;

	private final List<UserChildrenProvider> childrenProvider = new ArrayList<UserChildrenProvider>();

	private static final String UI_USER_CHILDREN_PROVIDER = "ch.fusun.baron.serverapp.userchildren"; //$NON-NLS-1$
	private static final String PROVIDER = "provider"; //$NON-NLS-1$

	/**
	 * Constructor
	 * 
	 */
	public UsersView() {
		ReInjector.getInstance().reInject(this);
		IConfigurationElement[] config = Platform.getExtensionRegistry()
				.getConfigurationElementsFor(UI_USER_CHILDREN_PROVIDER);
		try {
			for (IConfigurationElement e : config) {
				UserChildrenProvider bean = (UserChildrenProvider) e
						.createExecutableExtension(PROVIDER);
				childrenProvider.add(bean);
				ReInjector.getInstance().reInject(bean);
			}
		} catch (CoreException ex) {
			System.err.println("Handle this error with log or so: " //$NON-NLS-1$
					+ ex.getMessage());
		}
	}

	/**
	 * @return the {@link UserService}
	 */
	public UserService getUserService() {
		return userService;
	}

	/**
	 * @param userService
	 *            the new user Service
	 */
	@Inject
	public void setUserService(UserService userService) {
		this.userService = userService;
		this.userService.addDataListener(this);
		if (viewer != null) {
			viewer.setInput(new UsersViewRoot(userService));
		}
	}

	/**
	 * @param turnService
	 *            The new turn service
	 */
	@Inject
	public void setTurnService(TurnService turnService) {
		this.turnService = turnService;
		this.turnService.addDataListener(this);
	}

	/**
	 * The content provider class is responsible for providing objects to the
	 * view. It can wrap existing objects in adapters or simply return objects
	 * as-is. These objects may be sensitive to the current input of the view,
	 * or ignore it and always show the same content (like Task List, for
	 * example).
	 */
	class ViewContentProvider implements ITreeContentProvider {

		@Override
		public void dispose() {
			// Do nothing
		}

		@Override
		public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
			// TODO Auto-generated method stub
		}

		@Override
		public Object[] getChildren(Object parentElement) {
			if (parentElement instanceof UsersViewRoot) {
				return ((UsersViewRoot) parentElement).getUsers();
			}
			if (parentElement instanceof User) {
				return getAllChildren((User) parentElement).toArray();
			}
			return null;
		}

		@Override
		public Object getParent(Object element) {
			return null;
		}

		@Override
		public boolean hasChildren(Object element) {
			if (element instanceof UsersViewRoot) {
				return ((UsersViewRoot) element).getUsers().length > 0;
			}
			if (element instanceof User) {
				List<Object> children = getAllChildren((User) element);
				if (!children.isEmpty()) {
					return true;
				}
			}
			return false;
		}

		private List<Object> getAllChildren(User user) {
			List<Object> children = new ArrayList<Object>();
			for (UserChildrenProvider provider : childrenProvider) {
				List<?> temp = provider.getChildren(user);
				if (temp != null) {
					children.addAll(temp);
				}
			}
			return children;
		}

		@Override
		public Object[] getElements(Object inputElement) {

			return getChildren(inputElement);
		}
	}

	class ViewLabelProvider extends LabelProvider {

		@Override
		public String getText(Object element) {
			if (element instanceof User) {
				User user = (User) element;
				return user.getName();
			}
			return element == null ? "" : element.toString(); //$NON-NLS-1$
		}

		@Override
		public Image getImage(Object obj) {
			if (obj instanceof User) {
				if (getUserService().isLoggedIn((User) obj)) {
					return Activator.getImageDescriptor(Activator.USER_ONLINE)
							.createImage();
				}
				return Activator.getImageDescriptor(Activator.USER_OFFLINE)
						.createImage();
			}
			for (UserChildrenProvider provider : childrenProvider) {
				Image image = provider.getImage(obj);
				if (image != null) {
					return image;
				}
			}
			return null;
		}
	}

	private Display getDisplay() {
		return getSite().getShell().getDisplay();
	}

	@Override
	public void onTurn() {
		getDisplay().syncExec(new Runnable() {
			@Override
			public void run() {
				viewer.refresh();
			}
		});
	}

	@Override
	public void userLoggedOut(final User user) {
		getDisplay().syncExec(new Runnable() {
			@Override
			public void run() {
				viewer.refresh();
			}
		});
	}

	@Override
	public void userLoggedIn(final User user) {
		getDisplay().syncExec(new Runnable() {
			@Override
			public void run() {
				viewer.refresh();
			}
		});
	}

	@Override
	public void userCreated(final User user) {
		getDisplay().syncExec(new Runnable() {
			@Override
			public void run() {
				viewer.refresh();
			}
		});
	}

	/**
	 * This is a call back that will allow us to create the viewer and
	 * initialize it.
	 */
	@Override
	public void createPartControl(Composite parent) {
		viewer = new TreeViewer(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL);
		contentProvider = new ViewContentProvider();
		viewer.setContentProvider(contentProvider);
		viewer.setLabelProvider(new ViewLabelProvider());
		// Provide the input to the ContentProvider
		viewer.setInput(new UsersViewRoot(userService));
	}

	/**
	 * Passing the focus request to the viewer's control.
	 */
	@Override
	public void setFocus() {
		viewer.getControl().setFocus();
	}

	@Override
	public void dataChanged(DataUpdate update) {
		getDisplay().syncExec(new Runnable() {
			@Override
			public void run() {
				viewer.refresh();
			}
		});
	}
}